#pragma once
#include "CGIPrefix.h"
#include "Runtime/Base/Object.h"
#include "Runtime/Math/IntPoint.h"
#include "Runtime/Math/IntVector.h"
#include "Runtime/Math/Color.h"

namespace Alice{
	enum class EConservativeRasterization : uint8
	{
		Disabled,
		Overestimated,
	};
	// Defines which channel is valid for each pixel format
	enum class EPixelFormatChannelFlags : uint8
	{
		R = 1 << 0,
		G = 1 << 1,
		B = 1 << 2,
		A = 1 << 3,
		RG = R | G,
		RGB = R | G | B,
		RGBA = R | G | B | A,

		None = 0,
	};
	ENUM_CLASS_FLAGS(EPixelFormatChannelFlags);
	// Hints for some RHIs that support subpasses
	enum class ESubpassHint : uint8
	{
		// Regular rendering
		None,

		// Render pass has depth reading subpass
		DepthReadSubpass,

		// Mobile defferred shading subpass
		DeferredShadingSubpass,

		// Mobile MSAA custom resolve subpass. Includes DepthReadSubpass.
		CustomResolveSubpass,
	};
	enum class EPixelFormatCapabilities : uint32
	{
		None             = 0,
		Texture1D        = 1ull << 1,
		Texture2D        = 1ull << 2,
		Texture3D        = 1ull << 3,
		TextureCube      = 1ull << 4,
		RenderTarget     = 1ull << 5,
		DepthStencil     = 1ull << 6,
		TextureMipmaps   = 1ull << 7,
		TextureLoad      = 1ull << 8,
		TextureSample    = 1ull << 9,
		TextureGather    = 1ull << 10,
		TextureAtomics   = 1ull << 11,
		TextureBlendable = 1ull << 12,
		TextureStore     = 1ull << 13,

		Buffer           = 1ull << 14,
		VertexBuffer     = 1ull << 15,
		IndexBuffer      = 1ull << 16,
		BufferLoad       = 1ull << 17,
		BufferStore      = 1ull << 18,
		BufferAtomics    = 1ull << 19,

		UAV              = 1ull << 20,
		TypedUAVLoad     = 1ull << 21,
		TypedUAVStore    = 1ull << 22,

		TextureFilterable = 1ull << 23,

		AnyTexture       = Texture1D | Texture2D | Texture3D | TextureCube,

		AllTextureFlags  = AnyTexture | RenderTarget | DepthStencil | TextureMipmaps | TextureLoad | TextureSample | TextureGather | TextureAtomics | TextureBlendable | TextureStore,
		AllBufferFlags   = Buffer | VertexBuffer | IndexBuffer | BufferLoad | BufferStore | BufferAtomics,
		AllUAVFlags      = UAV | TypedUAVLoad | TypedUAVStore,

		AllFlags         = AllTextureFlags | AllBufferFlags | AllUAVFlags
	};
	ENUM_CLASS_FLAGS(EPixelFormatCapabilities);
	/** 
	* Information about a pixel format. The majority of this structure is valid after static init, however RHI does keep
	* some state in here that is initialized by that module and should not be used by general use programs that don't
	* have RHI (so noted in comments).
	*/
	struct FPixelFormatInfo
	{
		FPixelFormatInfo() = delete;
		FPixelFormatInfo(
			EPixelFormat InUnrealFormat,
			const TCHAR* InName,
			int32 InBlockSizeX,
			int32 InBlockSizeY,
			int32 InBlockSizeZ,
			int32 InBlockBytes,
			int32 InNumComponents,
			bool  InSupported);

		const TCHAR*				Name;
		EPixelFormat				UnrealFormat;
		int32						BlockSizeX;
		int32						BlockSizeY;
		int32						BlockSizeZ;
		int32						BlockBytes;
		int32						NumComponents;

		/** Per platform cabilities for the format (initialized by RHI module - invalid otherwise) */
		EPixelFormatCapabilities	Capabilities = EPixelFormatCapabilities::None;

		/** Platform specific converted format (initialized by RHI module - invalid otherwise) */
		uint32						PlatformFormat{ 0 };

		/** Whether the texture format is supported on the current platform/ rendering combination */
		uint8						Supported : 1;

		// If false, 32 bit float is assumed (initialized by RHI module - invalid otherwise)
		uint8						bIs24BitUnormDepthStencil : 1;
		
		/** 
		* Get 2D/3D image/texture size in bytes. This is for storage of the encoded image data, and does not adjust
		* for any GPU alignment/padding constraints. It is also not valid for tiled or packed mip tails (i.e. cooked mips 
		* for consoles). Only use these when you know you're working with bog standard textures/images in block based pixel formats.
		*/
		uint64 Get2DImageSizeInBytes(uint32 InWidth, uint32 InHeight) const;
		uint64 Get2DTextureMipSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InMipIndex) const;
		uint64 Get2DTextureSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InMipCount) const;
		uint64 Get3DImageSizeInBytes(uint32 InWidth, uint32 InHeight, uint32 InDepth) const;
		uint64 Get3DTextureMipSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InTextureDepth, uint32 InMipIndex) const;
		uint64 Get3DTextureSizeInBytes(uint32 InTextureWidth, uint32 InTextureHeight, uint32 InTextureDepth, uint32 InMipCount) const;
		
		/**
		* Get the number of compressed blocks necessary to hold the given dimensions.
		*/
		uint64 GetBlockCountForWidth(uint32 InWidth) const;
		uint64 GetBlockCountForHeight(uint32 InHeight) const;
	};
	extern FPixelFormatInfo GPixelFormats[EPixelFormat::PF_NUM];		// Maps members of EPixelFormat to a FPixelFormatInfo describing the format.
	
	ECGIAccess CGIGetDefaultResourceState(ETextureCreateFlags InUsage, bool bInHasInitialData);
	VkFormat UEToVkBufferFormat(EVertexElementType Type);
	struct FResourceArrayUploadInterface;
	struct CGIResourceCreateInfo
	{
		CGIResourceCreateInfo(const TCHAR* InDebugName)
			: DebugName(InDebugName)
		{
		}
		// for CreateBuffer calls
		CGIResourceCreateInfo(const TCHAR* InDebugName, FResourceArrayUploadInterface* InResourceArray)
			: CGIResourceCreateInfo(InDebugName)
		{
			ResourceArray = InResourceArray;
		}
		// for CreateBuffer calls
		FResourceArrayUploadInterface* ResourceArray = nullptr;
		// whether to create an RHI object with no underlying resource
		bool bWithoutNativeResource = false;
		const TCHAR* DebugName;
		const TCHAR* ClassName;
		const TCHAR*  OwnerName;
	};
	struct CGIBufferDesc
	{
		uint32 Size{};
		uint32 Stride{};
		EBufferUsageFlags Usage{};

		CGIBufferDesc() = default;
		CGIBufferDesc(uint32 InSize, uint32 InStride, EBufferUsageFlags InUsage)
			: Size  (InSize)
			, Stride(InStride)
			, Usage (InUsage)
		{}

		static CGIBufferDesc Null()
		{
			return CGIBufferDesc(0, 0, EBufferUsageFlags::NullResource);
		}

		bool IsNull() const
		{
			if (EnumHasAnyFlags(Usage, EBufferUsageFlags::NullResource))
			{
				// The null resource descriptor should have its other fields zeroed, and no additional flags.
				//check(Size == 0 && Stride == 0 && Usage == BUF_NullResource);
				return true;
			}

			return false;
		}
	};
	struct FRHIDescriptorHandle
	{
		FRHIDescriptorHandle() = default;
		FRHIDescriptorHandle(ERHIDescriptorHeapType InType, uint32 InIndex)
			: Index(InIndex)
			, Type((uint8)InType)
		{
		}
		FRHIDescriptorHandle(uint8 InType, uint32 InIndex)
			: Index(InIndex)
			, Type(InType)
		{
		}

		inline uint32                 GetIndex() const { return Index; }
		inline ERHIDescriptorHeapType GetType() const { return (ERHIDescriptorHeapType)Type; }
		inline uint8                  GetRawType() const { return Type; }

		inline bool IsValid() const { return Index != MAX_uint32 && Type != (uint8)ERHIDescriptorHeapType::Invalid; }

	private:
		uint32    Index{ MAX_uint32 };
		uint8     Type{ (uint8)ERHIDescriptorHeapType::Invalid };
	};
    class CGIResource : public Object{
    public:
        CGIResource(ECGIResourceType inResourceType);
        virtual ~CGIResource();
        const ECGIResourceType mResourceType;
        uint8 mbCommitted : 1;
        uint8 mbAllowExtendLifetime : 1;
    };
	class CGIVertexDeclaration : public CGIResource
	{
	public:
		CGIVertexDeclaration() : CGIResource(RRT_VertexDeclaration) {}
	};
	class CGISamplerState : public CGIResource 
	{
	public:
		CGISamplerState() : CGIResource(RRT_SamplerState) {}
		virtual bool IsImmutable() const { return false; }
		virtual FRHIDescriptorHandle GetBindlessHandle() const { return FRHIDescriptorHandle(); }
	};
	struct FImmutableSamplerState
	{
		FImmutableSamplerState() : ImmutableSamplers(MaxImmutableSamplers, nullptr)
		{}

		void Reset()
		{
			for (uint32 Index = 0; Index < MaxImmutableSamplers; ++Index)
			{
				ImmutableSamplers[Index] = nullptr;
			}
		}

		bool operator==(const FImmutableSamplerState& rhs) const
		{
			return ImmutableSamplers == rhs.ImmutableSamplers;
		}

		bool operator!=(const FImmutableSamplerState& rhs) const
		{
			return ImmutableSamplers != rhs.ImmutableSamplers;
		}

		std::vector<CGISamplerState*> ImmutableSamplers;
	};
	class CGIRasterizerState : public CGIResource
	{
	public:
		CGIRasterizerState() : CGIResource(RRT_RasterizerState) {}
		virtual bool GetInitializer(struct FRasterizerStateInitializerRHI& Init) { return false; }
	};

	class CGIDepthStencilState : public CGIResource
	{
	public:
		CGIDepthStencilState() : CGIResource(RRT_DepthStencilState) {}
#if ENABLE_RHI_VALIDATION
		FExclusiveDepthStencil ActualDSMode;
#endif
		virtual bool GetInitializer(struct FDepthStencilStateInitializerRHI& Init) { return false; }
	};
	class CGIBlendState : public CGIResource
	{
	public:
		CGIBlendState() : CGIResource(RRT_BlendState) {}
		virtual bool GetInitializer(class FBlendStateInitializerRHI& Init) { return false; }
	};
    class CGIViewableResource : public CGIResource{
    public:
		ECGIAccess TrackedAccess;
    	CGIViewableResource(ECGIResourceType InResourceType, ECGIAccess InAccess)
			: CGIResource(InResourceType)
			, TrackedAccess(InAccess)
    	{}
    };
	
	
	class CGIBuffer:public CGIResource
	{
	public:
		CGIBufferDesc Desc;
		CGIBuffer(CGIBufferDesc const& InDesc)
		: CGIResource(RRT_Buffer)//, ERHIAccess::Unknown /* TODO (RemoveUnknowns): Use InitialAccess from descriptor after refactor. */)
		, Desc(InDesc)
		{}
		/** @return The number of bytes in the buffer. */
		uint32 GetSize() const { return Desc.Size; }

		/** @return The stride in bytes of the buffer. */
		uint32 GetStride() const { return Desc.Stride; }

		/** @return The usage flags used to create the buffer. */
		EBufferUsageFlags GetUsage() const { return Desc.Usage; }
	};
	class CGIShader : public CGIResource
	{
	public:
		CGIShader(ECGIResourceType InResourceType, EShaderFrequency InFrequency)
			:CGIResource(InResourceType)
		{
			
		}
	};
	class CGIGraphicsShader : public CGIShader
	{
	public:
		explicit CGIGraphicsShader(ECGIResourceType InResourceType, EShaderFrequency InFrequency)
			: CGIShader(InResourceType, InFrequency) {}
	};
	class CGIVertexShader : public CGIGraphicsShader
	{
	public:
		CGIVertexShader() : CGIGraphicsShader(RRT_VertexShader, SF_Vertex) {}
	};
	class CGIPixelShader : public CGIGraphicsShader
	{
	public:
		CGIPixelShader() : CGIGraphicsShader(RRT_PixelShader, SF_Pixel) {}
	};
	class CGIGraphicsPipelineState : public CGIResource 
	{
	public:
		CGIGraphicsPipelineState() : CGIResource(RRT_GraphicsPipelineState) {}

		inline void SetSortKey(uint64 InSortKey) { SortKey = InSortKey; }
		inline uint64 GetSortKey() const { return SortKey; }

		virtual CGIGraphicsShader* GetShader(EShaderFrequency Frequency) const = 0;

	private:
		uint64 SortKey = 0;

#if ENABLE_RHI_VALIDATION
		friend class FValidationContext;
		friend class FValidationRHI;
		FExclusiveDepthStencil DSMode;
#endif
	};
	class CGIComputePipelineState : public CGIResource
	{
	public:
		CGIComputePipelineState() : CGIResource(RRT_ComputePipelineState){
		}
	};
	struct FBoundShaderStateInput
	{
		inline FBoundShaderStateInput():mVertexDeclaration(nullptr),mVertexShader(nullptr),mPixelShader(nullptr) {}
		inline FBoundShaderStateInput
		(
			CGIVertexDeclaration* InVertexDeclarationRHI
			, CGIShader* InVertexShaderRHI
			, CGIShader* InPixelShaderRHI
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
			, FRHIGeometryShader* InGeometryShaderRHI
	#endif
		)
			: mVertexDeclaration(InVertexDeclarationRHI)
			, mVertexShader(InVertexShaderRHI)
			, mPixelShader(InPixelShaderRHI)
	#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
			, GeometryShaderRHI(InGeometryShaderRHI)
	#endif
		{
		}
		CGIVertexDeclaration* mVertexDeclaration;
		CGIShader* mVertexShader;
		CGIShader* mPixelShader;
	};
    class CGIViewport : public CGIResource{
    public:
        CGIViewport() : CGIResource(RRT_Viewport) {}
        /**
         * Returns access to the platform-specific native resource pointer.  This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeSwapChain() const { return nullptr; }
        /**
         * Returns access to the platform-specific native resource pointer to a backbuffer texture.  This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeBackBufferTexture() const { return nullptr; }
        /**
         * Returns access to the platform-specific native resource pointer to a backbuffer rendertarget. This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all.
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason
         */
        virtual void* GetNativeBackBufferRT() const { return nullptr; }

        /**
         * Returns access to the platform-specific native window. This is designed to be used to provide plugins with access
         * to the underlying resource and should be used very carefully or not at all. 
         *
         * @return	The pointer to the native resource or NULL if it not initialized or not supported for this resource type for some reason.
         * AddParam could represent any additional platform-specific data (could be null).
         */
        virtual void* GetNativeWindow(void** AddParam = nullptr) const { return nullptr; }

        /**
         * Sets custom Present handler on the viewport
         */
        virtual void SetCustomPresent(class FRHICustomPresent*) {}

        /**
         * Returns currently set custom present handler.
         */
        virtual class FRHICustomPresent* GetCustomPresent() const { return nullptr; }


        /**
         * Ticks the viewport on the Game thread
         */
        virtual void Tick(float DeltaTime) {}

        virtual void WaitForFrameEventCompletion() { }

        virtual void IssueFrameEvent() { }
        void Draw( bool bShouldPresent = true );
    };
}
